<?php

namespace App\Service;


use App\Models\Admin;
use App\Models\SiteMsg;
use App\Models\User;
use App\Service\Page;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Crypt;

class Option
{

    // 设置传递参数信息
    // 主要参数：
    //       1、传递的 $request 参数，
    //       2、传递到模板页面的 $assign 参数，
    //       3、传递缓存的参数
    //       4、传递的响应参数
    //       5、token处理
    //       6、session与cookie的处理

    // 很多时候某些服务不能传递参数
    // 所以新建这么一个类，用于传递参数（先设置参数，然后在特定的地方使用这个参数）

    // 需要用的
    // 使用静态属性 方便多次实例化的时候，只有一次传值：其他时候实例化同样可以调用该属性的值
    protected static $options;
    protected static $app;
    protected static $request;
    protected static $data;
    protected static $whole;  // 整理过的所有数据，下次使用的时候直接调用，不在处理（主要包含传递的参数，header，token，cookie，请求的参数等）
    //protected static $myUser;  // 用户在控制器判断

    // 页面
    protected static $assign;
    // 5、token处理（主要用于在外部：如控制器与逻辑操作以及中间件生成缓存等）
    protected static $token;
    // 6、session与cookie的处理（主要用于在外部：如控制器与逻辑操作以及中间件生成缓存等）
    protected static $user;

    protected $error;
    protected $code;

    // 构造方法
    public function __construct() { }

    // 设置 request
    public function request()
    {
        return self::$request;
    }

    #######################################################################################################
    ## 设置数据
    // 设置app
    public function setApp($request)
    {
        if(empty(self::$request)){
            self::$request = $request;
        }
        if(empty(self::$data)){

            // TODO 暂时没有加密解密，只有一般操作
            // 不加密：必须传递 参数 U4TIRP87hGoJhcyM，且值为mZwTzXgOawVV1GJJ
            $request_is_encode = 'mZwTzXgOawVV1GJJ'; // 默认没有加密， 需要加密则注释
            //$request_is_encode = $request->header('U4TIRP87hGoJhcyM');
            // 如果传递参数不对或者没有传递该参数，则表示传递的参数是加密的，需要解密
            if($request_is_encode != 'mZwTzXgOawVV1GJJ'){
                define('IS_ENCODE', 1); // 设置为加密状态
                // 将传递过来的参数，进行解密操作
                $contents1 = $request->all();
                $contents2 = $request->input('contents');
                $contents2 = json_decode($this->decode($contents2), true);
                $contents  = empty($contents2) ? $contents1 : array_merge($contents1, $contents2);

                $contents3 = $request->header('content-token');
                $contents3 = json_decode($this->decode($contents3), true);
                $contents  = empty($contents3) ? $contents : array_merge($contents3, $contents);

            } else{
                define('IS_ENCODE', 0); // 设置状态为不加密
                $contents = $request->all();
            }

            //self::$data = $contents;
            //self::$data = array_merge($request->all(), $request->route()->parameters);
            self::$data = array_merge($contents, $request->route()->parameters);
        }

        return $this;
    }

    // 获取语言
    public function lang()
    {
        // 确定语言
        if(empty(self::$whole['web_lang'])){
            //  如果指定语言，则保存 cookie
            //  如果没有指定语言，则或者 cookie，没有获取到则为英文
            $lang = self::$request->header('content-language');
            $lang = $lang ?: $this->getData('web_language', null, 'trim');
            if(!empty($lang)){
                Cookie::queue('web_language', $lang);
            } else{
                $lang = Cookie::get('web_language', 'en');
            }

            //  如果是后台 默认为中文
            $module = $this->getData('module', null, 'trim');
            if($module == 'backend'){
                $lang = 'cn';
            }

            self::$whole['web_lang'] = strtolower($lang);
        } else{
            $lang = self::$whole['web_lang'];
        }

        return $lang;
    }

    // 获取 翻译结果
    public function translate($msg)
    {
        // 平时不使用翻译的时候 直接返回数据
        // return $msg;

        // 获取所有的数据
        $msgDb = new SiteMsg();
        $langs = $msgDb->isCache(true)->getAll();
        $langs = array_column($langs, null, 'field');

        // 如果没有找到翻译 直接返回
        $info = $langs[$msg] ?? [];
        if(empty($info)){
            return str_replace('_', ' ', $msg).'.';
        }

        // 确定语言
        $lang = $this->lang();

        // 执行翻译
        if($lang == 'cn'){
            $rst = $info['cn_msg'];
        } elseif($lang == 'tc'){
            $rst = $info['tc_msg'];
        } else{
            $rst = $info['en_msg'];
        }
        // 如果没有找到翻译 默认返回英文翻译
        if(empty($rst)){
            $rst = $info['en_msg'];
        }
        // 如果实在没有翻译 则直接返回
        if(empty($rst)){
            return str_replace('_', ' ', $msg).'.';
        }

        return $rst;
    }

    // 设置参数
    public function setData($data)
    {
        self::$data = $data;

        return $this;
    }

    // 设置参数
    public function setUser($user)
    {
        self::$user = $user;

        return $this;
    }

    // 设置参数的值
    //   如果参数存在 则替换
    public function setField($field, $value = null)
    {
        if(empty(self::$data)){
            self::$data = [];
        }
        self::$data[$field] = $value;
        // 这里已经整个过了的，不在整理
        self::$whole['data'][$field] = $value;

        return $this;
    }

    // 改变参数的键名称
    // 设置新参数的值 并 删除以前的那个参数
    public function changeData($field, $to_field, $value = null)
    {
        if(empty(self::$data)){
            self::$data = [];
        }
        if(is_null($value)){
            $value = self::$data[$field] ?? null;
        }

        // 设置新参数的值 并 删除以前的那个参数
        self::$data[$to_field] = $value;
        unset(self::$data[$field]);

        return $this;
    }

    #######################################################################################################
    ## 取得数据

    // 获取错误信息
    public function getError()
    {
        return $this->error;
    }

    // 获取错误编码
    public function getCode()
    {
        return $this->code;
    }

    // 获取当前ip
    public function getIp()
    {
        return self::$request->ip();
    }

    // $func 数据转换函数  如果为 true 则为 null 的时候也返回默认值
    public function getData($name = '', $def = null, $func = '')
    {
        $data = self::$data;
        if(empty($name)){
            return $data;
        }
        if(!isset($data[$name])){
            return $def;
        }
        // 如果已经整理过的数据 则直接返回
        if(isset(self::$whole['data'][$name])){
            return self::$whole['data'][$name];
        }
        /*if($data[$name]){
            if(empty($def)){
                return $data[$name];
            } else{
                return $def;
            }
        }*/
        // 传递为空字符串 接收为 null
        if(is_null($data[$name])){
            return $def;
        }

        // 特殊字段  已经存在了，直接返回
        if(($name == 'page') && (!empty($this->page))){
            return $this->page;
        } elseif(($name == 'rows') && (!empty($this->rows))){
            return $this->rows;
        }

        // 已经经过安全过滤操作 （过滤xss）
        $value = $data[$name];
        if(empty($func)){
            return $value;
        }

        // php 7种数据类型
        // 一般不是数组或对象的操作
        // 默认操作方法是  trim
        // 以下处理返回的数据
        $return = null;
        if(in_array($func, ['trim', 'intval'])){
            $this->throwReturn(is_object($value) || is_array($value), '参数错误，'.$name.'不能是数组或对象！');

            // 如果是默认方法 trim 同时又是传递的是数字 则不做任何处理
            //$this->throwReturn(is_numeric($value) && $func=='trim','参数错误，'.$name.'必须是字符串！');
            if($func == 'trim' && is_numeric($value)){
                $return = $value;
            }
        } elseif(in_array($func, ['is_array', 'json_encode'])){
            $this->throwReturn(!is_array($value), '参数错误，'.$name.'必须是数组或对象');
            if($func == 'is_array'){
                $return = $value;
            } elseif($func == 'json_encode'){
                $return = $this->jsonChinese($value);
            }
        }

        // 如果返回的没有处理 重新处理一下
        if(is_null($return)){
            $return = $func($value);
        }
        self::$whole['data'][$name] = $return;

        return $return;
    }

    // 获取Header中的参数值
    public function getHeader(string $key = '', $default = null)
    {
        // HTTP请求头信息的名称不区分大小写，并且 _ 会自动转换为 -
        $result = self::$request->header($key);
        if(empty($result)) return $default;

        return $result;
    }

    // 获取用户信息
    public function getUser(string $key = '')
    {
        $user = self::$user;
        if($key){
            return $user[$key] ?? null;
        }

        return $user;
    }

    #######################################################################################################
    // 制作翻页
    public function getPages($total, $page, $rows)
    {
        $result = (new Page($total, $page, $rows))->show();

        return $result;
    }

    // 加载模板输出
    public function dispaly($template, $vars = [], $checkMobile = true)
    {
        $vars = empty($vars) ? self::$assign : (self::$assign ? array_merge(self::$assign, $vars) : $vars);
        $vars = empty($vars) ? [] : $vars;
        if($checkMobile){
            $tpl_file = ($this->isMobile() ? 'wap/' : 'pc/').$template;
        } else{
            $tpl_file = $template;
        }

        return view($tpl_file, $vars);
    }

    // 返回数据
    public function assign($name, $value = '')
    {
        if(is_array($name)){
            self::$assign = array_merge(self::$assign, $name);
        } else{
            self::$assign[$name] = $value;
        }

        return $this;
    }

    public function getAssign($name, $value = '')
    {
        return self::$assign[$name] ?? $value;
    }

    #######################################################################################################
    ## token、cookie、session等的处理
    // 获取用户以及权限
    public function token($type = 'user', $tokenField = 'token')
    {
        $myUser = self::$token;
        if(!isset(self::$token)){
            $myUser = [];
            $token  = self::$request->header($tokenField);
            if(!empty($token)){
                // TODO 为了避免解析以前的token报错（The payload is invalid.）
                try{
                    $myUser = Crypt::decryptString($token);
                    $myUser = json_decode($myUser, true);
                } catch(\Exception $e){
                    $myUser = [];
                }
                if(empty($myUser)){
                    return $this->_returnFail('token过期，请重新登录！！', LOGIN_ERROR);
                }
                if(empty($myUser['ctime'])){
                    return $this->_returnFail('token过期，请重新登录！！', LOGIN_ERROR);
                }

                // 查看登录的用户信息
                if($type == 'user'){
                    $userDb = new User();
                } elseif($type == 'admin'){
                    $userDb = new Admin();
                }

                $info = $userDb->isCache(true)->oneFind(['id' => $myUser['id']]);
                //$this->throwIf(empty($info), '用户不存在！！');
                //$this->throwIf($info['verify'] === 0, '你暂未通过审核！！');
                if(empty($info)){
                    return $this->_returnFail('登录过期，请重新登录！！', LOGIN_ERROR);
                }
                if($info['status'] == -1){
                    return $this->_returnFail('登录过期，请重新登录！！', LOGIN_ERROR);
                }
                if($info['status'] < 0){
                    return $this->_returnFail('登录过期，请重新登录！！', LOGIN_ERROR);
                }
                if($info['status'] == 0){
                    return $this->_returnFail('对不起，你已经被禁用！！', LOGIN_ERROR);
                }

                $myUser = is_object($info) ? $info->toArray() : $info;
                // 组合
                $myUser['uid'] = $myUser['id'];
            }

            self::$token = $myUser;
        }

        /*if(empty($field)){
            return $myUser;
        }

        return $myUser[$field] ?? null;*/

        return $myUser;
    }

    // cookie、session
    public function session($field = '')
    {
        $myUser = self::$user;
        if(!isset(self::$user)){
            $myUser = [];
            $uid    = self::$request->session()->get('uid');
            if(!empty($uid)){
                // 查看登录的用户信息
                $userDb = new User();
                $info   = $userDb->isCache(true)->oneFind(['id' => $uid]);
                if(empty($info)){
                    return $this->_returnFail('登录过期，请重新登录！！', LOGIN_ERROR);
                }
                if($info['verify'] === 0){
                    //return $this->_returnFail('你暂未通过审核！');
                }

                $myUser = is_object($info) ? $info->toArray() : $info;
                // 组合
                $myUser['uid'] = $myUser['id'];
            }

            self::$user = $myUser;
        }

        if(empty($field)){
            return $myUser;
        }

        return $myUser[$field] ?? null;
    }

    // 判断是否是手机端
    public function isMobile():bool
    {
        // 如果已经整理过的数据 则直接返回
        if(isset(self::$whole['request']['is_mobile'])){
            return self::$whole['request']['is_mobile'];
        }

        // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
        if(isset ($_SERVER['HTTP_X_WAP_PROFILE'])){
            $return = true;
        }

        // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
        if(empty($return) && isset ($_SERVER['HTTP_VIA'])){
            $return = stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;// 找不到为flase,否则为TRUE
        }

        // 判断手机发送的客户端标志,兼容性有待提高
        if(empty($return) && isset ($_SERVER['HTTP_USER_AGENT'])){
            $clientkeywords = array(
                'mobile',
                'nokia',
                'sony',
                'ericsson',
                'mot',
                'samsung',
                'htc',
                'sgh',
                'lg',
                'sharp',
                'sie-',
                'philips',
                'panasonic',
                'alcatel',
                'lenovo',
                'iphone',
                'ipod',
                'blackberry',
                'meizu',
                'android',
                'netfront',
                'symbian',
                'ucweb',
                'windowsce',
                'palm',
                'operamini',
                'operamobi',
                'openwave',
                'nexusone',
                'cldc',
                'midp',
                'wap',
            );
            // 从HTTP_USER_AGENT中查找手机浏览器的关键字
            if(preg_match("/(".implode('|', $clientkeywords).")/i", strtolower($_SERVER['HTTP_USER_AGENT']))){
                $return = true;
            }
        }

        if(empty($return) && isset ($_SERVER['HTTP_ACCEPT'])){ // 协议法，因为有可能不准确，放到最后判断
            // 如果只支持wml并且不支持html那一定是移动设备
            // 如果支持wml和html但是wml在html之前则是移动设备
            if((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html')))){
                $return = true;
            }
        }

        // 整理返回
        if(empty($return)){
            $return = false;
        }
        self::$whole['request']['is_mobile'] = $return;

        return $return;
    }

    #######################################################################################################
    ## 加解密
    // 加密
    public function encode($data)
    {
        $data = is_array($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;

        // 加密配置
        $iv   = '';
        $type = 'DES-ECB';
        $type = 'AES-128-CBC';
        $type = 'AES-192-CBC';
        $conf = Config::get('encrypt');
        $key  = substr($conf['key'], 0, 24);
        if($type == 'AES-128-CBC' || $type == 'AES-192-CBC'){
            //$iv = '1234567812345678';
            $iv = substr($conf['iv'], 0, 16);
        }

        $str = openssl_encrypt($data, $type, $key, 0, $iv);

        return base64_encode($str);
    }

    // 解密
    public function decode($str)
    {
        if(empty($str)){
            return '';
        }

        $string = base64_decode($str);

        // 加密配置
        $iv   = '';
        $type = 'DES-ECB';
        $type = 'AES-128-CBC';
        $type = 'AES-192-CBC';
        $conf = Config::get('encrypt');
        $key  = substr($conf['key'], 0, 24);
        if($type == 'AES-128-CBC' || $type == 'AES-192-CBC'){
            //$iv = '1234567812345678';
            $iv = substr($conf['iv'], 0, 16);
        }


        return openssl_decrypt($string, $type, $key, 0, $iv);
        //return openssl_decrypt($string, $type, $key, OPENSSL_RAW_DATA, $iv);
    }

    #######################################################################################################
    ## 异常

    // 抛出异常
    public function throwReturn($condition, $msg, $code = 400)
    {
        if($condition){
            throw new \RuntimeException($msg, $code);
        }
    }

    // 抛出异常
    public function throwIf($condition, $msg, $code = 400)
    {
        if($condition){
            throw new \RuntimeException($msg, $code);
        }
    }

    // 在初始化中，不会抛出异常，只能使用终止程序
    public function dieIf($condition, $msg, $code = 400, $is_encode = IS_ENCODE)
    {
        if($condition){
            $json = ['msg' => $msg, 'code' => $code, 'data' => [],];
            if($is_encode){
                //$json = ['data' => $this->encode($data)];
            } else{
                $json['access'] = 1; //不加密
            }

            $json = json_encode($json);
            die($json);
        }
    }

    #######################################################################################################
    // 验证
    protected function validate($data, $rules, array $message = [], array $attr = [])
    {
        if(empty($rules)){
            return true;
        }

        // 验证
        $validator = \Validator::make($data, $rules, $message, $attr);
        if($validator->fails()){
            $messages = $validator->errors();

            return $this->_returnFail($messages->first());
        }

        return true;
    }

    // 转化json,中文不转化
    protected function jsonChinese(array $data = [])
    {
        return json_encode($data, JSON_UNESCAPED_UNICODE);
    }

    // 返回错误
    protected function _returnFail($error, $code = 400)
    {
        $this->error = $error;
        $this->code  = $code;

        return false;
    }

}
